package com.xinda.xiaoxing.service.impl;

import cn.hutool.core.util.BooleanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xinda.xiaoxing.config.opc.Connector;
import com.xinda.xiaoxing.entity.influxdb.OPC;
import com.xinda.xiaoxing.entity.oracle.XxngPoint;
import com.xinda.xiaoxing.service.IXxngPointService;
import com.xinda.xiaoxing.service.OPCService;
import com.xinda.xiaoxing.dao.influxdb.InfluxdbDao;
import org.jinterop.dcom.common.JIException;
import org.jinterop.dcom.core.JIVariant;
import org.openscada.opc.lib.common.AlreadyConnectedException;
import org.openscada.opc.lib.common.NotConnectedException;
import org.openscada.opc.lib.da.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.net.UnknownHostException;
import java.util.*;


@Service
@Scope("prototype")
public class OPCServiceImpl implements OPCService{
    Logger logger = LoggerFactory.getLogger(OPCServiceImpl.class);

    @Autowired
    ApplicationContext applicationContext;
    @Autowired
    IXxngPointService xxngPointService;
    @Autowired
    InfluxdbDao influxdbDao;

    int turn=0;
    long reconnectionTimes = 0;//重连次数

    @Override
    public Map<String, Item> initItems(String serverName, String prefix) {
        Map<String, Item> idItemMap = null;
        Server server= (Server) applicationContext.getBean(serverName);
        String[] itemIds;

        QueryWrapper<XxngPoint> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("SERVER",serverName);
        queryWrapper.eq("ENABLE",1);
        List<XxngPoint> points = xxngPointService.list(queryWrapper);
        itemIds=new String[points.size()];

        if(serverName.contains("kepServer")){
            for(int i=0;i<itemIds.length;i++){
                XxngPoint point = points.get(i);
                itemIds[i]=point.getChannel()+"."+point.getDevice()+"."+point.getItem();
            }
        }else if(serverName.contains("talkServer")){
            for(int i=0;i<itemIds.length;i++){
                XxngPoint point = points.get(i);
                itemIds[i]="["+point.getDevice()+"]"+point.getItem();
            }
        }

        try {
            Group group = server.addGroup("default");
            idItemMap = group.addItems(itemIds);
        } catch (UnknownHostException | NotConnectedException | JIException | DuplicateGroupException | AddFailedException e) {
            e.printStackTrace();
        }
        return idItemMap;
    }

    @Override
    public List<OPC> getOpcData(Server server, String serverName, Map<String, Item> idItemMap) {
        List<OPC> numResult=new ArrayList<>();
        for (String itemId : idItemMap.keySet()) {
            Item item = idItemMap.get(itemId);
            JIVariant itemValue;
            try {
                itemValue = item.read(false).getValue();
            } catch (Exception e) {//连接错误
                server.disconnect();
                e.printStackTrace();
                return numResult;
            }

            try {
                int type = itemValue.getType();
                String id = item.getId();
                OPC opc =new OPC();

                if(serverName.contains("kepServer")){
                    String[] splits = id.split("\\.");

//                    opc.setChannel(splits[0]);
                    opc.setDevice(splits[1]);
                    opc.setItem(splits[2]);
                }else if(serverName.contains("talkServer")){
                    String[] splits=id.split("%");
                    splits[0]=splits[0].replaceAll("\\[","").replaceAll("\\]","");

//                    opc.setChannel(".");
                    opc.setDevice(splits[0]);
                    opc.setItem(splits[1]);
                }

                switch (type){
                    case JIVariant.VT_R4:{//浮点型
//                        opc.setType("Float");
                        opc.setValue(new BigDecimal(String.valueOf(itemValue.getObjectAsFloat())).doubleValue());
                        numResult.add(opc);
                    };break;
                    case JIVariant.VT_I2:{//短整型
//                        opc.setType("Short");
                        opc.setValue((double) itemValue.getObjectAsShort());
                        numResult.add(opc);
                    };break;
                    case JIVariant.VT_I4:{//长整型
//                        opc.setType("Long");
                        opc.setValue((double) itemValue.getObjectAsInt());
                        numResult.add(opc);
                    }break;
                    case JIVariant.VT_UI2:{//字
//                        opc.setType("Word");
                        opc.setValue(itemValue.getObjectAsUnsigned().getValue().doubleValue());
                        numResult.add(opc);
                    }break;
                    case JIVariant.VT_UI4:{//双字
//                        opc.setType("DWord");
                        opc.setValue(itemValue.getObjectAsUnsigned().getValue().doubleValue());
                        numResult.add(opc);
                    }break;
                    case JIVariant.VT_BOOL:{//布尔
//                        opc.setType("Boolean");
                        opc.setValue(BooleanUtil.toDoubleObj(itemValue.getObjectAsBoolean()));
                        numResult.add(opc);
                    }break;
//                    case JIVariant.VT_BSTR:{//字符串
//                        opcStr.setValue(itemValue.getObjectAsString().getString());
//                        strResult.add(opcStr);
//                    }break;
                    case JIVariant.VT_UI1:{//Byte
//                        opc.setType("Byte");
                        opc.setValue((double) itemValue.getObjectAsUnsigned().getValue().byteValue());
                        numResult.add(opc);
                    }break;
//                    case JIVariant.VT_I1:{//字符
//                        opcStr.setServer(serverName);
//                        opcStr.setValue(String.valueOf(itemValue.getObjectAsChar()));
//                        strResult.add(opcStr);
//                    }break;
                    default:break;
                }
            } catch (JIException e) {
                e.printStackTrace();
            }

        }
        return numResult;
    }

    @Override
    public void syncOPCData(Server server, Connector connectionListener, String serverName,Map<String, Item> idItemMap) {
        if (connectionListener.isConnected()) {
            reconnectionTimes = 0;
            long startTime = System.currentTimeMillis();
            List<OPC> numList = getOpcData(server, serverName, idItemMap);
            long endTime = System.currentTimeMillis();
            long spend = endTime - startTime;

            logger.info(turn+":["+serverName+"]读取时间:" + spend + "ms," + "读取数目:" + numList.size());

            startTime = System.currentTimeMillis();
            influxdbDao.insertBatchByPoints(numList);
            endTime = System.currentTimeMillis();
            spend = endTime - startTime;
            logger.info(turn+":["+serverName+"]存储时间:" + spend + "ms");

            turn++;
            turn%=100;
        }
        else {
            reconnectionTimes++;
            logger.error(turn+":["+serverName+"]连接失败,尝试重新连接,重连次数:" + reconnectionTimes);
            try {
                server.connect();
            } catch (UnknownHostException e) {
                logger.error(turn+":["+serverName+"]重新连接失败,未知的host,即将重连");
                e.printStackTrace();
            } catch (AlreadyConnectedException e) {
                logger.error(turn+":["+serverName+"]重新连接失败,已存在连接,即将重连");
                e.printStackTrace();
            } catch (JIException e) {
                logger.error(turn+":["+serverName+"]重新连接失败,已存在连接,即将重连");
                e.printStackTrace();
            }
        }
    }
}
